package com.gsoe.problem.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.gsoe.base.client.UserClient;
import com.gsoe.base.enumeration.ProblemType;
import com.gsoe.base.exception.JudgeException;
import com.gsoe.base.exception.ProblemException;
import com.gsoe.base.exception.UserException;
import com.gsoe.base.model.PageResult;
import com.gsoe.base.model.Result;
import com.gsoe.base.utils.UserUtil;
import com.gsoe.problem.client.JudgeClient;
import com.gsoe.problem.dto.ProblemDTO;
import com.gsoe.problem.dto.ProblemParamDTO;
import com.gsoe.problem.mapper.ProblemMapper;

import com.gsoe.problem.po.*;
import com.gsoe.problem.service.*;
import com.gsoe.problem.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 题目表 服务实现类
 * </p>
 *
 * @author xiaoma
 * @since 2024-10-27
 */
@Service
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> implements ProblemService {
    @Autowired
    private ProblemAnswerService problemAnswerService;

    @Autowired
    private ProblemKnowledgeService problemKnowledgeService;

    @Autowired
    private ProblemMapper problemMapper;

    @Autowired
    private ProblemChooseService problemChooseService;

    @Autowired
    private JudgeClient judgeClient;

    @Autowired
    private UserClient userClient;


    @Override
    @Transactional
    public void addOrUpdateProblem(ProblemDTO problemDTO) {
        if (StringUtils.isNotEmpty(problemDTO.getProblemCode()) && StringUtils.isEmpty(problemDTO.getProblemCode())) {
            throw new ProblemException("请选择一种代码语言");
        }
        Long problemId = problemDTO.getId();
        Problem problem;
        if (problemId != null) {
            problem = getById(problemId);
            if (problem == null) {
                throw new ProblemException("无此题目");
            }
            problemAnswerService.remove(new QueryWrapper<ProblemAnswer>()
                    .eq("problem_id", problemId)
            );
            problemKnowledgeService.remove(new QueryWrapper<ProblemKnowledge>()
                    .eq("problem_id", problemId)
            );
            problemChooseService.remove(new QueryWrapper<ProblemChoose>()
                    .eq("problem_id", problemId)
            );
        } else {
            problem = new Problem();
        }
        BeanUtils.copyProperties(problemDTO, problem);
        saveOrUpdate(problem);


        //添加选项
        Integer type = problemDTO.getType();
        if (ProblemType.SINGLE == type || ProblemType.MULTIPLE == type) {
            List<String> optionDescribeList = problemDTO.getOptionDescribe();
            if (CollectionUtil.isEmpty(optionDescribeList)) {
                throw new ProblemException("请添加选项");
            }
            if (optionDescribeList.size() < 2 && ProblemType.MULTIPLE == type) {
                throw new ProblemException("多选请添加大于等于两个的选项");
            }
            List<ProblemChoose> problemChooseList = optionDescribeList.stream().map(s -> {
                ProblemChoose problemChoose = new ProblemChoose();
                problemChoose.setOptionDescribe(s);
                problemChoose.setProblemId(problem.getId());
                return problemChoose;
            }).toList();

            problemChooseService.saveBatch(problemChooseList);
        }


        //添加答案
        List<String> answerList = problemDTO.getAnswer();

        List<ProblemAnswer> problemAnswerList = answerList.stream().map((s -> {
            ProblemAnswer problemAnswer = new ProblemAnswer();
            problemAnswer.setProblemId(problem.getId());
            if (problemDTO.getType() == ProblemType.MULTIPLE) {
                char[] charArray = s.toCharArray();
                Arrays.sort(charArray);
                problemAnswer.setAnswer(String.valueOf(charArray));
            } else {
                problemAnswer.setAnswer(s);
            }
            return problemAnswer;
        })).toList();


        problemAnswerService.saveBatch(problemAnswerList);

        //添加知识点
        List<Long> knowledgeList = problemDTO.getKnowledgeList();
        List<ProblemKnowledge> problemKnowledgeList = List.of();
        if (knowledgeList != null) problemKnowledgeList = knowledgeList.stream().map(l -> {
            ProblemKnowledge problemKnowledge = new ProblemKnowledge();
            problemKnowledge.setProblemId(problem.getId());
            problemKnowledge.setKnowledgeId(l);
            return problemKnowledge;
        }).toList();


        problemKnowledgeService.saveBatch(problemKnowledgeList);

    }

    @Override
    public PageResult<ProblemVO> getProblemList(ProblemParamDTO dto) {
        //不公开题当前用户是否可见
        boolean b = StpUtil.hasPermission("problem_unvisible_look");
        Page<Problem> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        List<Long> knowledgeList = dto.getKnowledgeList();
        List<Long> problemIDsByKnowledge = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(knowledgeList)) {
            problemIDsByKnowledge = problemKnowledgeService.lambdaQuery()
                    .in(ProblemKnowledge::getKnowledgeId, knowledgeList)
                    .list().stream().map(ProblemKnowledge::getProblemId).toList();
        }
        if (CollectionUtil.isEmpty(problemIDsByKnowledge) && CollectionUtil.isNotEmpty(knowledgeList)) {
            return new PageResult<>();
        }


        page = lambdaQuery()
                .eq(dto.getType() != null, Problem::getType, dto.getType())
                //不公开题 没有看公开题权限的人 不能看
                .eq(!b, Problem::isVisible, true)
                .eq(dto.getProblemId() != null, Problem::getId, dto.getProblemId())
                .eq(dto.getDifficulty() != null, Problem::getDifficulty, dto.getDifficulty())
                .eq(Problem::isDeleted, false)
                .in(CollectionUtil.isNotEmpty(problemIDsByKnowledge), Problem::getId, problemIDsByKnowledge)
                .like(StringUtils.isNotEmpty(dto.getProblemDescribe()), Problem::getProblemDescribe, dto.getProblemDescribe())
                .page(page);

        Result<List<Long>> userProblemCollectResult = userClient.getUserAllProblemCollect(UserUtil.getLoginUser().getId());
        if(userProblemCollectResult.getCode()!=200){
            throw new UserException("getProblemList:" + userProblemCollectResult.getMsg());
        }

        List<Long> data = userProblemCollectResult.getData();

        return PageResult.of(page, p -> {
            ProblemVO problemVO = new ProblemVO();
            BeanUtils.copyProperties(p, problemVO);
            problemVO.setKnowledgeList(problemKnowledgeService.getKnowledgeListByProblemId(p.getId()));
            problemVO.setOptionDescribe(problemChooseService.list(new QueryWrapper<ProblemChoose>()
                    .eq("problem_id", p.getId())
            ).stream().map(ProblemChoose::getOptionDescribe).toList());
            problemVO.setAnswer(problemAnswerService.list(new QueryWrapper<ProblemAnswer>()
                    .eq("problem_id", p.getId())
            ).stream().map(ProblemAnswer::getAnswer).toList());
            if(CollectionUtil.isNotEmpty(data))problemVO.setCollected(data.contains(p.getId()));
            else problemVO.setCollected(false);
            return problemVO;
        });
    }

    @Override
    public List<Long> createRandProblemList(RandProblem randProblem) {
        List<Long> problemIDs = new ArrayList<>();
        Integer singleChooseNum = randProblem.getSingleChooseNum();
        Integer bigNum = randProblem.getBigNum();
        Integer multipleChooseNum = randProblem.getMultipleChooseNum();
        Integer judgeNum = randProblem.getJudgeNum();
        Integer fillNum = randProblem.getFillNum();
        if (singleChooseNum > 0) problemIDs.addAll(getRandIdListByType(ProblemType.SINGLE, singleChooseNum));
        if (fillNum > 0) problemIDs.addAll(getRandIdListByType(ProblemType.FILL, fillNum));
        if (judgeNum > 0) problemIDs.addAll(getRandIdListByType(ProblemType.JUDGE, judgeNum));
        if (multipleChooseNum > 0) problemIDs.addAll(getRandIdListByType(ProblemType.MULTIPLE, multipleChooseNum));
        if (bigNum > 0) problemIDs.addAll(getRandIdListByType(ProblemType.BIG, bigNum));
        return problemIDs;
    }

    @Override
    public ProblemRandomVO getRandomProblem(ProblemParamDTO problemParamDTO) {
        boolean b = StpUtil.hasPermission("problem_unvisible_look");
        List<Problem> list = list(new QueryWrapper<Problem>()
                .eq(problemParamDTO.getType() != null, "type", problemParamDTO.getType())
                .eq(problemParamDTO.getDifficulty() != null, "difficulty", problemParamDTO.getDifficulty())
                .eq(!b, "visible", true)
                .eq("deleted", false)
        );
        if (CollectionUtil.isEmpty(list)) {
            throw new ProblemException("暂无此类型题目，请重新选择");
        }
        Problem problem = list.get(new Random().nextInt(list.size()));
        ProblemRandomVO problemRandomVO = new ProblemRandomVO();
        BeanUtils.copyProperties(problem, problemRandomVO);
        if (problem.getType() == ProblemType.SINGLE || problem.getType() == ProblemType.MULTIPLE) {
            List<ProblemChoose> problemChooseList = problemChooseService.list(new QueryWrapper<ProblemChoose>()
                    .eq("problem_id", problem.getId())
            );
            problemRandomVO.setOptionDescribe(problemChooseList.stream().map(ProblemChoose::getOptionDescribe).toList());
        }


        return problemRandomVO;
    }

    @Override
    public ProblemVO getProblemById(Long id) {
        if (id == null) {
            throw new ProblemException("参数错误");
        }
        Problem problem = getById(id);
        if (problem == null) {
            throw new ProblemException("题目不存在");
        }
        if (problem.isDeleted()) {
            throw new ProblemException("题目已被删除");
        }
        ;
        if (!problem.isVisible()) {
            boolean b = StpUtil.hasPermission("problem_unvisible_look");
            if (!b) throw new ProblemException("该题未公开，暂无查看权限");
        }
        ProblemVO problemVO = new ProblemVO();
        BeanUtils.copyProperties(problem, problemVO);
        problemVO.setAnswer(problemAnswerService.list(new QueryWrapper<ProblemAnswer>()
                .eq("problem_id", id)
        ).stream().map(ProblemAnswer::getAnswer).toList());
        problemVO.setOptionDescribe(problemChooseService.list(new QueryWrapper<ProblemChoose>()
                .eq("problem_id", id)
        ).stream().map(ProblemChoose::getOptionDescribe).toList());
        problemVO.setKnowledgeList(problemKnowledgeService.getKnowledgeListByProblemId(id));
        return problemVO;
    }

    @Override
    @Transactional
    public void delByid(Long id) {
//        removeById(id);
//        problemChooseService.remove(new QueryWrapper<ProblemChoose>()
//                .eq("problem_id",id)
//        );
//
//        problemKnowledgeService.remove(new QueryWrapper<ProblemKnowledge>()
//                .eq("problem_id",id)
//        );
//
//        problemAnswerService.remove(new QueryWrapper<ProblemAnswer>()
//                .eq("problem_id",id)
//        );
        Problem problem = getById(id);
        if (problem == null) {
            return;
        }
        problem.setDeleted(true);
        updateById(problem);
    }

    @Override
    public List<Integer> getTypeNumberList() {
        List<Integer> typeNumberList = new ArrayList<>();
        typeNumberList.add(0);
        Map<Integer, List<Problem>> collected = list().stream().collect(Collectors.groupingBy(Problem::getType));
        for (int i = 1; i < 6; i++) {
            typeNumberList.add(collected.get(i) == null ? 0 : collected.get(i).size());
        }
        return typeNumberList;
    }

    @Override
    public UploadProblemVO uploadProblem(MultipartFile file, Boolean repeatSkip) {
        if (!(Objects.requireNonNull(file.getOriginalFilename()).endsWith(".xlsx"))) {
            throw new ProblemException("只支持excel文件");
        }
        List<List<String>> excelData = new ArrayList<>();
        try {
            excelData = getExcelData(file);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ProblemException("解析失败，报错：" + e.getMessage() + "  可能原因：数据超过23列");
        }

        UploadProblemVO uploadProblemVO = new UploadProblemVO();
        List<SkipOrFailVO> skipOrFailVOList = new ArrayList<>();
        List<ProblemVO> successProblemIds = new ArrayList<>();
        for (int i = 0; i < excelData.size(); i++) {
            String problemDescribe = excelData.get(i).get(0);
            if (StringUtils.isEmpty(problemDescribe)) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("题目描述为空");
                continue;
            }
            Problem problem = lambdaQuery()
                    .eq(Problem::getProblemDescribe, problemDescribe)
                    .eq(Problem::isDeleted, false)
                    .one();
            if (problem != null && repeatSkip) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("重复跳过");
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            ProblemDTO problemDTO = new ProblemDTO();
            if (problem != null) {
                BeanUtils.copyProperties(problem, problemDTO);
            }
            //题目基本信息
            problemDTO.setProblemDescribe(problemDescribe);
            problemDTO.setProblemCode(excelData.get(i).get(2));
            problemDTO.setCodeLanguage(excelData.get(i).get(3));
            problemDTO.setAnalysis(excelData.get(i).get(6));
            try {
                Integer difficulty = Integer.valueOf(excelData.get(i).get(7) == null ? "1" : excelData.get(i).get(7));
                if (difficulty <= 0 || difficulty > 3) {
                    throw new RuntimeException("难度请输入1~3整数");
                }
                problemDTO.setDifficulty(difficulty);

            } catch (Exception e) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("转换失败，可能不是数字");
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            String typeString = excelData.get(i).get(1);
            if (StringUtils.isEmpty(typeString)) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("类型为空");
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            if (typeString.equals("单选")) {
                problemDTO.setType(1);
            }
            if (typeString.equals("多选")) {
                problemDTO.setType(4);
            }
            if (typeString.equals("判断")) {
                problemDTO.setType(3);
            }
            if (typeString.equals("填空")) {
                problemDTO.setType(2);
            }
            if (typeString.equals("大题")) {
                problemDTO.setType(5);
            }
            if (problemDTO.getType() == null) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("类型不准确，请用下拉列表选择");
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            problemDTO.setVisible(true);

            //答案
            List<String> answerList = new ArrayList<>();
            String answer1 = excelData.get(i).get(4);
            String answer2 = excelData.get(i).get(5);
            if (StringUtils.isEmpty(answer1)) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason("第四列的答案为空");
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            answerList.add(answer1);
            if (StringUtils.isNotEmpty(answer2)) {
                answerList.add(answer2);
            }
            problemDTO.setAnswer(answerList);

            List<String> optionList = new ArrayList<>();
            for (int j = 8; j < 24; j++) {
                String s = excelData.get(i).get(j);
                if (StringUtils.isNotEmpty(s)) optionList.add(s);
            }
            problemDTO.setOptionDescribe(optionList);

            try {
                addOrUpdateProblem(problemDTO);
            } catch (Exception e) {
                SkipOrFailVO skipOrFailVO = new SkipOrFailVO();
                skipOrFailVO.setRowIndex(i + 1);
                skipOrFailVO.setReason(e.getMessage());
                skipOrFailVOList.add(skipOrFailVO);
                continue;
            }
            ProblemVO problemVO = new ProblemVO();
            BeanUtils.copyProperties(problemDTO, problemVO);
            successProblemIds.add(problemVO);
        }
        uploadProblemVO.setSuccessProblemIds(successProblemIds);
        uploadProblemVO.setSkipOrFailVOList(skipOrFailVOList);
        uploadProblemVO.setTotalProblemNumber(excelData.size());
        return uploadProblemVO;
    }

    @Override
    public List<ProblemNumberVO> getProblemNumberByDiff(Long userId) {
        /*用户刷题数*/
        Result<List<Judge>> problemNumberByDiff = judgeClient.getProblemNumberByDiff(userId);
        if (problemNumberByDiff.getCode() != 200) {
            throw new JudgeException(problemNumberByDiff.getMsg());
        }
        /*不同难度题目的总数*/
        List<Problem> problemList = problemMapper.getProblemDifficulty();
        Map<Integer, List<Problem>> totalNumberMap = problemList.stream().collect(Collectors.groupingBy(Problem::getDifficulty));
        int easyCountTotal = 0;
        int mediumCountTotal = 0;
        int hardCountTotal = 0;
        if (totalNumberMap.get(1) != null) {
            easyCountTotal = totalNumberMap.get(1).size();
        }
        if (totalNumberMap.get(2) != null) {
            mediumCountTotal = totalNumberMap.get(2).size();
        }
        if (totalNumberMap.get(3) != null) {
            hardCountTotal = totalNumberMap.get(3).size();
        }

        /*题目和其对应的难度*/
        Map<Long, Integer> problemIdDifficulty = problemList.stream().collect(Collectors.toMap(Problem::getId, Problem::getDifficulty));
        List<Judge> judgeList = problemNumberByDiff.getData();


        int easyCount=0;
        int mediumCount=0;
        int hardCount=0;
        for (Judge judge : judgeList) {
            Long problemId = judge.getProblemId();
            Integer difficulty = problemIdDifficulty.get(problemId);
            if (difficulty != null) {
                switch (difficulty) {
                    case 1:
                        easyCount++;
                        break;
                    case 2:
                        mediumCount++;
                        break;
                    case 3:
                        hardCount++;
                        break;
                }
            }
        }
        List<ProblemNumberVO> problemNumberVOList = new ArrayList<>();
        problemNumberVOList.add(new ProblemNumberVO(easyCount, easyCountTotal));
        problemNumberVOList.add(new ProblemNumberVO(mediumCount, mediumCountTotal));
        problemNumberVOList.add(new ProblemNumberVO(hardCount, hardCountTotal));
        return problemNumberVOList;
    }

    private List<List<String>> getExcelData(MultipartFile file) throws IOException {
        Workbook workbook = null;
        try {
            workbook = new XSSFWorkbook(file.getInputStream());
        } catch (Exception e) {
            throw new ProblemException(e.getMessage());
        }
        List<List<String>> data = new ArrayList<>();
        Sheet sheet = workbook.getSheetAt(0);
        Iterator<Row> rowIterator = sheet.iterator();
        boolean title = true;
        while (rowIterator.hasNext()) {
            Row row = rowIterator.next();
            if (title) {
                title = false;
                continue;
            }
            List<String> rowData = new ArrayList<>();
            for (int i = 0; i < 24; i++) {
                rowData.add(null);
            }
            Iterator<Cell> cellIterator = row.iterator();
            while (cellIterator.hasNext()) {
                Cell cell = cellIterator.next();
                rowData.set(cell.getColumnIndex(), getCellValueAsString(cell));
            }
            data.add(rowData);
        }
        workbook.close();
        return data;
    }

    private String getCellValueAsString(Cell cell) {
        switch (cell.getCellTypeEnum()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                double d = cell.getNumericCellValue();
                if ((int) d == d) {
                    int c = (int) d;
                    return String.valueOf(c);
                }
                return String.valueOf(d);
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }


    private List<Long> getRandIdListByType(Integer type, Integer num) {
        List<Problem> randIdListByType = problemMapper.getRandIdListByType(type, num);
        if (CollectionUtil.isEmpty(randIdListByType)) {
            return List.of();
        }
        return randIdListByType.stream().sorted(Comparator.comparingInt(Problem::getDifficulty)).map(Problem::getId).toList();
    }
}
